package com.stars.easyms.logger.support.logback.handler;

import ch.qos.logback.core.Appender;
import ch.qos.logback.core.OutputStreamAppender;
import com.stars.easyms.base.asynchronous.BaseBatchAsynchronousTask;
import com.stars.easyms.base.batch.BatchResult;
import com.stars.easyms.logger.support.logback.util.EasyMsEventByteHolder;

import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * <p>className: EasyMsAsynAppenderEncoder</p>
 * <p>description: 每个appender一个线程异步解析日志时间类</p>
 *
 * @author guoguifang
 * @date 2019-08-23 17:08
 * @since 1.3.0
 */
final class EasyMsAsynAppenderEncoder extends BaseBatchAsynchronousTask<EasyMsLoggingEvent> {

    private final EasyMsLoggingEventFactory easyMsLoggingEventFactory = EasyMsLoggingEventFactory.getInstance();

    private final Appender<EasyMsLoggingEvent> appender;

    private final EasyMsOutputStreamAppender easyMsOutputStreamAppender;

    private final Queue<Worker> lockQueue = new LinkedBlockingQueue<>();

    EasyMsAsynAppenderEncoder(OutputStreamAppender<EasyMsLoggingEvent> appender) {
        this.appender = appender;
        this.easyMsOutputStreamAppender = new EasyMsOutputStreamAppender(appender);
    }

    @Override
    protected BatchResult execute(List<EasyMsLoggingEvent> eventList) {
        // 日志需要保证顺序执行，当批量处理时为了保证顺序执行因此增加锁队列
        final Worker currWorker = getCurrentWorker();
        lockQueue.offer(currWorker);

        // 批量处理日志时间，为了增加处理性能使用批量处理，且该处不需要加锁
        List<byte[]> byteList = new ArrayList<>(eventList.size());
        EasyMsEventByteHolder.setEventByteList(byteList);
        try {
            eventList.forEach(event -> {
                appender.doAppend(event);
                easyMsLoggingEventFactory.offer(event);
            });
        } finally {
            EasyMsEventByteHolder.clear();
        }

        // 如果当前工作线程不在锁队列第一位则等待
        currWorker.await(() -> lockQueue.peek() != currWorker);

        // 如果当前工作线程在锁队列第一位时则执行写入日志操作
        easyMsOutputStreamAppender.write(byteList);

        // 将该锁队列第一位去掉并唤醒下一个工作线程
        lockQueue.poll();
        Worker nextWorker = lockQueue.peek();
        if (nextWorker != null) {
            nextWorker.signal();
        }
        return null;
    }

    @Override
    public boolean isOrder() {
        return true;
    }

    @Override
    protected int capacity() {
        return 10000000;
    }

    @Override
    protected int singleWorkerThreshold() {
        return 5000;
    }

    @Override
    protected int maxWorkerCount() {
        return 5;
    }

    @Override
    protected int batchExecuteMaxCountPerTime() {
        return 1000;
    }

    @Override
    protected long delayTime() {
        return 1;
    }

    @Override
    public String getName() {
        return appender.getName() + "-encoder";
    }

}